package com.nulldev.util.web.HttpServer3.extensions.http2.util;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.UncheckedIOException;
import java.lang.reflect.InvocationTargetException;
import java.util.function.BiConsumer;

import com.nulldev.util.data.Variables;
import com.nulldev.util.web.HttpServer3.extensions.http2.Http2Connection;
import com.nulldev.util.web.HttpServer3.extensions.http2.api_compat.HttpRequestDispatcher;
import com.nulldev.util.web.HttpServer3.extensions.http2.compat.HttpRequest;
import com.nulldev.util.web.HttpServer3.extensions.http2.compat.HttpRequestValidator;
import com.nulldev.util.web.HttpServer3.extensions.http2.compat.IOContainer;
import com.nulldev.util.web.HttpServer3.extensions.http2.compat.ResponseBuilder;
import com.nulldev.util.web.HttpServer3.extensions.http2.errors.CannotParseRequestException;

public class ServerThreadUtils {

	/**
	 * Still in experimental phase
	 */
	public static void http2Initialization(IOContainer socket, HttpRequestValidator requestValidator, HttpRequestDispatcher requestDispatcher)
			throws IOException, CannotParseRequestException, InvocationTargetException {
		final Http2Connection http2Connection = new Http2Connection(Variables.requireNonNullObject(socket, "socket"),
				Variables.requireNonNullObject(requestValidator, "requestValidator"), Variables.requireNonNullObject(requestDispatcher, "requestDispatcher"));
		http2Connection.setup();
		http2Connection.takeover();
	}

	/**
	 * This is a blocking method.
	 * 
	 * @param request         client request
	 * @param response        Response of this webserver corresponding to the client
	 *                        request. It checks if any handlers allows the use of
	 *                        tunneling
	 * @param httpReplyClient this consumer is only used whenever request exchange
	 *                        happens and when header {@code X-Vws-Exchange-Format}
	 *                        is set to value other than {@code raw} It takes remote
	 *                        server response as the 1st arg and output stream of
	 *                        the current webserver's socket as the 2nd arg.
	 */
	public static void socketTunnelInitialization(HttpRequest request, ResponseBuilder response, BiConsumer<ResponseBuilder, OutputStream> httpReplyClient)
			throws IOException, InterruptedException {
		String tunnelDest = response.getHeaders().getHeader("X-Vws-Raw-Tunnel");
		boolean exchangeRequest = Boolean.parseBoolean(response.getHeaders().getHeader("X-Vws-Exchange-Once"));
		// TODO: sanitize dest

		int colonIndex = 0;
		int port = 80;
		if ((colonIndex = tunnelDest.lastIndexOf(':')) != -1) {
			port = Integer.parseInt(tunnelDest.substring(colonIndex + 1));
			tunnelDest = tunnelDest.substring(0, colonIndex);
		}

		Thread wsReaderThread = null;
		try (HttpTunnel tunnel = new HttpTunnel(tunnelDest, port)) {
			tunnel.open();
			IOContainer socket = request.getSocket();
			BufferedInputStream tunnelIs = new BufferedInputStream(tunnel.getSocket().getInputStream());
			BufferedOutputStream tunnelOs = new BufferedOutputStream(tunnel.getSocket().getOutputStream());

			if (exchangeRequest) {
				// Just reply client, without modifying their response.
				httpReplyClient.accept(tunnel.send(request), socket.getOutputStream());
			}

			(wsReaderThread = new Thread("Tunnel: WS Reader Thread") {
				@Override
				public void run() {
					try {
						HttpTunnel.streamToUntilClose(tunnelIs, socket.getOutputStream());
					} catch (IOException e) {
						throw new UncheckedIOException(e);
					}
				}
			}).start();

			HttpTunnel.streamToUntilClose(socket.getInputStream(), tunnelOs);
		} finally {
			if (wsReaderThread != null) {
				wsReaderThread.interrupt();
				wsReaderThread.join();
			}
		}
	}
}
